﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;
using System.Xml;
using System.Threading;
using Erebos.CsharpEngine;

namespace Erebos.GameScreenFiles
{
    public struct VertexPositionColorNormal
    {
        public Vector3 Position;
        public Color Color;
        public Vector3 Normal;

        public readonly static VertexDeclaration VertexDeclaration = new VertexDeclaration
        (
            new VertexElement(0, VertexElementFormat.Vector3, VertexElementUsage.Position, 0),
            new VertexElement(sizeof(float) * 3, VertexElementFormat.Color, VertexElementUsage.Color, 0),
            new VertexElement(sizeof(float) * 3 + 4, VertexElementFormat.Vector3, VertexElementUsage.Normal, 0)
        );
    }

    public struct VertexMultitextured
    {
        public Vector3 Position;
        public Vector3 Normal;
        public Vector4 TextureCoordinate;
        public Vector4 TexWeights;
        public Vector4 TexWeights2;

        public static int SizeInBytes = (3 + 3 + 4 + 4 + 4) * sizeof(float);
        public readonly static VertexDeclaration VertexDeclaration = new VertexDeclaration
        (
            new VertexElement(0, VertexElementFormat.Vector3, VertexElementUsage.Position, 0),
            new VertexElement(sizeof(float) * 3, VertexElementFormat.Vector3, VertexElementUsage.Normal, 0),
            new VertexElement(sizeof(float) * 6, VertexElementFormat.Vector4, VertexElementUsage.TextureCoordinate, 0),
            new VertexElement(sizeof(float) * 10, VertexElementFormat.Vector4, VertexElementUsage.TextureCoordinate, 1),
            new VertexElement(sizeof(float) * 14, VertexElementFormat.Vector4, VertexElementUsage.TextureCoordinate, 2)
        );
    }

    public struct TextureInfos
    {
        private string _fileName;
        public string FileName
        {
            get { return _fileName; }
            set { _fileName = value; }
        }

        private int _min;
        public int Minimum
        {
            get { return _min; }
            set { _min = value; }
        }

        private int _max;
        public int Maximum
        {
            get { return _max; }
            set { _max = value; }
        }

        public TextureInfos(string name, int min, int max)
        {
            _fileName = name;
            _max = max;
            _min = min;
        }
    }

    public class World
    {
        private GraphicsDevice _device;
        private Effect _effect;
        private Chunk[,] _chunks;
        private int _chunkWidth;
        private int _chunkHeight;

        private List<WorldObjects.Object> _objects = new List<WorldObjects.Object>();
        public List<WorldObjects.Object> Objects
        {
            get { return _objects; }
        }

        private EventScript _test;

        private bool _generate;
        public bool IsGenerate
        {
            get { return _generate; }
        }

        private int _mapID;

        private void Generate()
        {
            _generate = true;
            _objects = new List<WorldObjects.Object>();

            //Load XML File
            XmlDocument doc = new XmlDocument();
            doc.Load("Content/maps/" + _mapID + "/Map.xml");
            XmlNode root = doc.DocumentElement;
            List<Chunk> chunks = new List<Chunk>();
            int width = 0, height = 0;
            List<TextureInfos> textureInfos = new List<TextureInfos>();
            foreach (XmlNode node in root.ChildNodes)
            {
                if (node.Name == "size")
                {
                    foreach (XmlNode node2 in node.ChildNodes)
                    {
                        if (node2.Name == "width")
                            width = Convert.ToInt32(node2.InnerText);
                        else if (node2.Name == "height")
                            height = Convert.ToInt32(node2.InnerText);
                        else if (node2.Name == "cwidth")
                            _chunkWidth = Convert.ToInt32(node2.InnerText);
                        else if (node2.Name == "cheight")
                            _chunkHeight = Convert.ToInt32(node2.InnerText);
                    }
                }

                if (node.Name == "texturemap")
                {
                    foreach (XmlNode node2 in node.ChildNodes)
                    {
                        string name = "";
                        int min = 0, max = 0;

                        if (node2.Name == "texture")
                        {
                            name = node2.Attributes["name"].InnerText;
                            foreach (XmlNode node3 in node2.ChildNodes)
                            {
                                if (node3.Name == "min")
                                    min = Convert.ToInt32(node3.InnerText);
                                if (node3.Name == "max")
                                    max = Convert.ToInt32(node3.InnerText);
                            }
                        }

                        textureInfos.Add(new TextureInfos(name, min, max));
                    }
                }

                if (node.Name == "chunks")
                {
                    foreach (XmlNode node2 in node.ChildNodes)
                    {
                        if (node2.Name == "chunk")
                        {
                            int x = 0, y = 0, cx = 0, cy = 0;
                            string path = "", texturePath = "";
                            foreach (XmlNode chunk in node2.ChildNodes)
                            {
                                if (chunk.Name == "x")
                                    x = Convert.ToInt32(chunk.InnerText);
                                else if (chunk.Name == "y")
                                    y = Convert.ToInt32(chunk.InnerText);
                                else if (chunk.Name == "cx")
                                    cx = Convert.ToInt32(chunk.InnerText);
                                else if (chunk.Name == "cy")
                                    cy = Convert.ToInt32(chunk.InnerText);
                                else if (chunk.Name == "path")
                                    path = chunk.InnerText;
                                else if (chunk.Name == "tmap")
                                    texturePath = chunk.InnerText;
                            }
                            chunks.Add(new Chunk(MainGame.LoadContent<Texture2D>("maps/" + _mapID + "/" + path), MainGame.LoadContent<Texture2D>("maps/" + _mapID + "/" + texturePath), x, y, cx, cy, _device, _effect, textureInfos));
                        }
                    }
                }


                if (node.Name == "objects")
                {
                    foreach (XmlNode obj in node.ChildNodes)
                    {
                        if (obj.Name == "smokeplume")
                        {
                            Vector3 position = new Vector3(0, 0, 0);
                            Vector3 gravity = new Vector3(0, 0, 0);

                            foreach (XmlNode conf in obj.ChildNodes)
                            {
                                if (conf.Name == "vector3")
                                {
                                    string use = conf.Attributes["use"].InnerText;
                                    foreach (XmlNode vector in conf.ChildNodes)
                                    {
                                        switch (use)
                                        {
                                            case "position":
                                                if (vector.Name == "x")
                                                    position.X = Convert.ToInt32(vector.InnerText);
                                                if (vector.Name == "y")
                                                    position.Y = Convert.ToInt32(vector.InnerText);
                                                if (vector.Name == "z")
                                                    position.Z = Convert.ToInt32(vector.InnerText);
                                                break;
                                            case "gravity":
                                                if (vector.Name == "x")
                                                    gravity.X = Convert.ToInt32(vector.InnerText);
                                                if (vector.Name == "y")
                                                    gravity.Y = Convert.ToInt32(vector.InnerText);
                                                if (vector.Name == "z")
                                                    gravity.Z = Convert.ToInt32(vector.InnerText);
                                                break;
                                        }
                                    }
                                }
                            }

                            _objects.Add(new WorldObjects.SmokePlumeObject(position, gravity));
                        }
                        else if (obj.Name == "fire")
                        {
                            Vector3 position = new Vector3(0, 0, 0);
                            Vector3 size = new Vector3(0, 0, 0);
                            int intensivität = 4800;
                            foreach (XmlNode conf in obj.ChildNodes)
                            {
                                if (conf.Name == "vector3")
                                {
                                    string use = conf.Attributes["use"].InnerText;
                                    foreach (XmlNode vector in conf.ChildNodes)
                                    {
                                        switch (use)
                                        {
                                            case "position":
                                                if (vector.Name == "x")
                                                    position.X = Convert.ToInt32(vector.InnerText);
                                                if (vector.Name == "y")
                                                    position.Y = Convert.ToInt32(vector.InnerText);
                                                if (vector.Name == "z")
                                                    position.Z = Convert.ToInt32(vector.InnerText);
                                                break;
                                            case "size":
                                                if (vector.Name == "x")
                                                    size.X = Convert.ToInt32(vector.InnerText);
                                                if (vector.Name == "y")
                                                    size.Y = Convert.ToInt32(vector.InnerText);
                                                if (vector.Name == "z")
                                                    size.Z = Convert.ToInt32(vector.InnerText);
                                                break;
                                        }
                                    }
                                }
                                else if (conf.Name == "int")
                                {
                                    string use = conf.Attributes["use"].InnerText;
                                    switch (use)
                                    {
                                        case "intensivität":
                                            intensivität = Convert.ToInt32(conf.InnerText);
                                            break;
                                    }
                                }
                            }

                            _objects.Add(new WorldObjects.FireObject(position, size, intensivität));
                        }
                        else if (obj.Name == "firework")
                        {
                            Vector3 position = new Vector3(0, 0, 0);
                            Vector2 velocity = new Vector2(80, 60);
                            int minHeight = 50, time = 2;
                            foreach (XmlNode conf in obj.ChildNodes)
                            {
                                if (conf.Name == "vector3")
                                {
                                    string use = conf.Attributes["use"].InnerText;
                                    foreach (XmlNode vector in conf.ChildNodes)
                                    {
                                        switch (use)
                                        {
                                            case "position":
                                                if (vector.Name == "x")
                                                    position.X = Convert.ToInt32(vector.InnerText);
                                                if (vector.Name == "y")
                                                    position.Y = Convert.ToInt32(vector.InnerText);
                                                if (vector.Name == "z")
                                                    position.Z = Convert.ToInt32(vector.InnerText);
                                                break;
                                        }
                                    }
                                }
                                else if (conf.Name == "vector2")
                                {
                                    string use = conf.Attributes["use"].InnerText;
                                    foreach (XmlNode vector in conf.ChildNodes)
                                    {
                                        switch (use)
                                        {
                                            case "velocity":
                                                if (vector.Name == "x")
                                                    velocity.X = Convert.ToInt32(vector.InnerText);
                                                if (vector.Name == "y")
                                                    velocity.Y = Convert.ToInt32(vector.InnerText);
                                                break;
                                        }
                                    }
                                }
                                else if (conf.Name == "int")
                                {
                                    string use = conf.Attributes["use"].InnerText;
                                    switch (use)
                                    {
                                        case "minHeight":
                                            minHeight = Convert.ToInt32(conf.InnerText);
                                            break;
                                        case "time":
                                            time = Convert.ToInt32(conf.InnerText);
                                            break;
                                    }
                                }
                            }

                            _objects.Add(new WorldObjects.FireworkObject(position, velocity, minHeight, time));
                        }
                    }
                }
            }

            _chunks = new Chunk[width, height];

            foreach (Chunk c in chunks)
                _chunks[c.ChunkX, c.ChunkY] = c;

            _generate = false;
        }

        public World(int mapID, GraphicsDevice device, Effect effect)
        {
            _device = device;
            _effect = effect;
            Camera.SetUpCamera(device, this);
            _test = new EventScript("Content/maps/1/event1.cs");

            _mapID = mapID;
            Generate();
        }

        public void ChangeMap(int id)
        {
            _mapID = id;
            Thread thr = new Thread(Generate);
            thr.Start();
        }

        public void CreateExplosion(Vector3 position)
        {
            _objects.Add(new WorldObjects.ExplosionObject(position));
        }

        public float GetHeightData(Vector3 position)
        {
            //Find Chunk
            Chunk c = null;
            foreach (Chunk chunk in _chunks)
                if (chunk.IsInsert(new Vector2(position.X, position.Z)))
                    c = chunk;

            if (c == null)
                return 0;
            else
                return c.GetHeightData(position - new Vector3(c.X, 0, c.Y));
        }

        public void Draw(GameTime gameTime)
        {
            foreach (Chunk c in _chunks)
                c.Draw();

            foreach (WorldObjects.Object o in _objects)
                o.Draw(gameTime);
        }

        public void Update(GameTime gameTime)
        {
            _test.Run();

            float timeDifference = (float)gameTime.ElapsedGameTime.TotalMilliseconds / 1000.0f;
            Camera.Update(timeDifference);

            foreach (Chunk c in _chunks)
                c.Update(gameTime);

            int i = 0;

            while (i < _objects.Count)
            {
                if (_objects[i].Free)
                {
                    // Remove objects at the end of their life.
                    //_objects.RemoveAt(i);
                    i++;
                }
                else
                {
                    // Update the object.
                    _objects[i].Update(gameTime);
                    // Advance to the next object.
                    i++;
                }
            }
        }
    }
}
